/**
 * Copyright [2012-2013] [Guðmundur Björn Birkisson]
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. Copyright [2012-2013] [Guðmundur Björn Birkisson]
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * ---------------------------------------------------------------------------
 * 
 * Class name:  StepController
 * 
 * Description of class:
 * 
 * Static class that controls stepping in Karel programs.
 * 
 */

package karel.gamelogic;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import karel.editors.texteditor.TextEditor;

public class StepController {

    private static class StepHelper extends Thread {

        private boolean isRunning;
        private volatile boolean kickNeeded;
        
        public StepHelper(String name) {
            super(name);
            isRunning = true;
            kickNeeded = false;
        }
        
        public synchronized void kick() {
            kickNeeded = true;
        }
        
        public synchronized void waitForKick() {
            try {
                wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(StepController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
        public synchronized void stopThread() {
            isRunning = false;
        }
        
        @Override
        public void run() {
            while(isRunning) {
                if(kickNeeded) {
                    synchronized(this) {
                        kickNeeded = false;
                        notifyAll();
                    }
                }
            }
        }
        
    }
    
    private volatile static TextEditor editor;
    private static StepHelper stepThread;
    public volatile static boolean isStepping = false;
    private volatile static boolean stopProgram = false;

    public static void setTextEditor(TextEditor textEditor) {
        editor = textEditor;
    }
    
    public static void startStepThread() {
        stepThread = new StepHelper("StepThread");
        stepThread.start();
    }
    
    public static void killStepThread() {
        stepThread.stopThread();
    } 
    
    public static void kick() {
        stepThread.kick();
    }
    
    public static void stopProgram() {
        stopProgram = true;
        stepThread.waitForKick();
    }
    
    /**
     * Only to be called from morpho
     */
    public static void programStopped() {
        stepThread.kick();
    }
    
    public static void programStoppedWithFlag() {
        stopProgram = false;
        programStopped();
    }

    /**
     * Only to be called from morpho
     * @param line
     * @throws IOException 
     */
    public static void step(int line) throws IOException {
        if(stopProgram) {
            stopProgram = false;
            throw new IOException("Program stopped!");
        } else if(isStepping) {
            editor.highlightLine(line);
            stepThread.waitForKick();
        }
    }
    
    public static void stopExec() throws IOException {
        throw new IOException("Program stopped!");
    }
    
    public static void error(Object obj) {
        System.out.println(obj);
    }
    
    public static void printException(Exception e) {
        e.printStackTrace();
    }
}
